//= require <framework/w3c/events/DataTransfer>
//= require <framework/w3c/events/Event>
//= require <framework/w3c/events/CustomEvent>
//= require <framework/w3c/events/UIEvent>
//= require <framework/w3c/events/MouseEvent>
//= require <framework/w3c/events/DragEvent>
//= require <framework/w3c/events/PhaseType>

/*The EventTarget interface is implemented by all the objects which could be event targets 
 in an implementation which supports an event flow. The interface allows registration and 
 removal of event listeners, and dispatch of events to an event target.*/
if(typeof w3c === "undefined") {w3c={}};
if(typeof w3c.EventTarget === "undefined") {w3c.EventTarget={}};


w3c.EventTarget = {
	/*
	 Registers an event listener, depending on the useCapture parameter, on 
	 the capture phase of the DOM event flow or its target and bubbling phases
	 
	 useCapture indicates that the user wishes to add the event listener for 
	 the capture phase only, i.e. this event listener will not be triggered during 
	 the target and bubbling phases. If false, the event listener will only be 
	 triggered during the target and bubbling phases.
	 */
	addEventListener : function(type, callback, capture, scope){
		capture = (typeof capture == "boolean") ? capture : false;
		scope   = (typeof scope == "object") ? scope : this;
		
		if (!this.subscribers) {this.subscribers = {}};
		if (!this.subscribers[type]) {this.subscribers[type] = []};
		
		var subscribers = this.subscribers[type];
		for(var i=0; i<=subscribers.length-1; i++) {
			if(subscribers[i].callback === callback) {return;}
		};
		
		subscribers.push({type:type, callback:callback, capture:capture});
		if (this._internal_nativeevents.indexOf(type) >= 0) {
			this._internal_addElementListener(type, callback, capture);
		}
	},
	
	removeEventListener : function(type, callback, capture){
		var subscribers = this.subscribers[type]||[];
		subscribers.remove(function(subscriber) {
        	return (subscriber.type === type && subscriber.callback === callback && subscriber.capture === capture);
		});
		if (this._internal_nativeevents.indexOf(type) >= 0) {
			return this._internal_removeElementListener(type, callback, capture);
		}
	},
	
	dispatchEvent : function(/*w3c.Event*/event) {
		if(!event || !(event instanceof w3c.Event)) {
			throw new TypeError("DOMException:\n" + this.namespace + "#dispatchEvent(event),\
			expected an argument which should be an instance of an initialized w3c.Event object,\
			instead of arguments: " + arguments.toString());
		};
		var self		= this;
		var ancestors 	= [];
        var target 		= this.element;
		var type 		= event.type;

		while(target) {
			target=target.parentNode;
			if(!target || target.nodeName.toLowerCase()=="html"){ break; }
			if(!target.nodeType || target.nodeType !== 1) {continue;}
			
			var ancestor = this.prototypeof(target);
			if(ancestor instanceof w3c.Node) {
				ancestors.push(target);	
			}
		};

		//TODO: add this method to w3c.EventTarget.prototype chain when finalized.
		var CAPTURE_EVENT = function() {
			//if(event.defaultPrevented()) {return;}
			var ancestorNodes = ancestors.reverse();
			for(var i=0; i<=ancestorNodes.length-1; i++) {
				var ancestor = self.prototypeof(ancestorNodes[i]);
				if (ancestor.subscribers && ancestor.subscribers[type]) {
					var subscribers = (ancestor.subscribers[type]||[]);
					for(var j=0; j<=subscribers.length-1; j++) {
						if(subscribers[j].capture == true) {
							event.eventPhase = w3c.PhaseType.CAPTURING_PHASE;
							event.phaseName="CAPTURING_PHASE";
							event.target=self;
							event.currentTarget = ancestor;
							subscribers[j].callback.call(ancestor, event, self);
							if (event.defaultPrevented()) {
								break;
							}
						}
					};
					if(event.defaultPrevented()){continue;}
					if(event.propagationStopped()){break;}
				}
			};
	 	};
		
		//TODO: add this method to w3c.EventTarget.prototype chain when finalized.
		var AT_TARGET = function(){
			//if(event.defaultPrevented()) {return;}
			if (self.subscribers && self.subscribers[type]) {
				var subscribers = (self.subscribers[type] || []);
					subscribers = subscribers.reverse();
				for(var j=0; j<=subscribers.length-1; j++) {
					if(!subscribers[j].capture && !event.defaultPrevented()) {
						event.eventPhase = w3c.PhaseType.AT_TARGET;
						event.phaseName="AT_TARGET";
						event.target=self;
						event.currentTarget = self;
						subscribers[j].callback.call(self, event, self);
						if (event.defaultPrevented()) {
							break;
						}
					}
				}
			}
	 	};
		
		//TODO: add this method to w3c.EventTarget.prototype chain when finalized.
		var BUBBLE_EVENT = function(){
			//if(event.defaultPrevented()) {return;}
			var ancestorNodes = ancestors;
			for(var i=0; i<=ancestorNodes.length-1; i++) {
				var ancestor = self.prototypeof(ancestorNodes[i]);
				if (ancestor.subscribers && ancestor.subscribers[type]) {
					var subscribers = (ancestor.subscribers[type] || []);
					for(var j=0; j<=subscribers.length-1; j++) {
						if(!subscribers[j].capture) {
							event.eventPhase = w3c.PhaseType.BUBBLING_PHASE;
							event.phaseName="BUBBLING_PHASE";
							event.target=self;
							event.currentTarget = ancestor;
							subscribers[j].callback.call(ancestor, event, self);
							if(event.defaultPrevented()) {break;}
						}
					};
					if(event.defaultPrevented()){continue;}
					if(event.propagationStopped()){break;}
				}
			};
	 	};
		
		CAPTURE_EVENT();
		if(event.propagationStopped()) {return;}
		
		AT_TARGET();
		if(event.propagationStopped()) {return;}
		
		BUBBLE_EVENT();
	},
	
	addEventListenerNS : function(){
		throw new Error("w3c.EventTarget#addEventListenerNS() not yet implemented");
	},
	
	removeEventListenerNS : function(){
		throw new Error("w3c.EventTarget#removeEventListenerNS() not yet implemented");
	},
	
	addEventDelegate : function(type, callback, capture){
		var self = this;
		if (type.indexOf(":") >= 0) {
			var parts = type.split(":");
			var className = parts[0].replace(".","");
			var eventType = parts[1];
			
			this.addEventListener(eventType, function(e){
				if(e && e.target && self.hasClass(className,e.target)) {
					callback(e);
				}
			}, capture);
		}
	},
	
	prototypeof : function(node) {
		if(node instanceof w3c.Node) {
			return node;
		}
		else if(node.prototype) {return node.prototype;}
		else {
			return node;
		}
	},
	
	_internal_nativeevents : [
		"DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", "DOMNodeRemovedFromDocument",
		"DOMNodeInsertedIntoDocument", "DOMAttrModified", "DOMCharacterDataModified",
		"DOMFocusIn", "DOMFocusOut", "DOMActivate", 
		"keyup", "keydown", "keypress", "click", "mousedown", "mouseup", "mouseover", "mousemove", 
		"mouseout", "load", "unload", "abort", "error", "change", "submit", "reset", "focus", 
		"select", "blur", "resize", "scroll", "selectstart", 
		"drag", "dragstart", "dragover", "dragenter", "dragleave", "dragend", "drop"
	],
	
	_internal_removeElementListener : function(type, callback, capture){
		var element = this.element;
		if(element.removeEventListener){ // Mozilla, Netscape, Firefox
			return element.removeEventListener(type, callback, capture);
		} else { // IE
			return element.detachEvent('on'+type, callback);
		}
	},
	
	_internal_addElementListener : function(type, callback, capture){
		var element = this.element;
		if(element.addEventListener){ // Mozilla, Netscape, Firefox
			return element.addEventListener(type, callback, capture);
		} else { // IE
			return element.attachEvent(('on'+type), callback);
		}
	}
};


